"""
lasted edited at 2018-6-12 by Stone
1. The method to construct the shape of the objects is so complicated, and the data to specify the shape of objects
is rather more, so I separate them here.
2. "xx" class in main code is correspondingly named "xxShape" here.
3. All the unit of length is meter.
4. Create the function "move" and "moveTo" to make it more explicit to construct the buildings,
instead of the old way that straightly set the pos. (like the real operation in 3D modeling software)


data source:
    car: https://wenku.baidu.com/view/f6a7573a67ec102de3bd890f.html
    bus: https://wenku.baidu.com/view/06e54dd6b9f3f90f76c61bec.html
"""

from visual import *
pos = vector


# ----- functions -----
def move(obj, dis):  # dis = displacement
    """move the obj against the displacement"""
    obj.pos += dis


def move_to(obj, new_pos):
    """move the obj to the new_pos"""
    obj.pos = new_pos


class CarShape:

    def __init__(self):pass
    shape = {
        "car": {"length": 4.6, "width": 1.7, "height": 1.4},
        "bus": {"length": 12.0, "width":2.5, "height": 3.3}
    }


class CrossRoadShape:

    def __init__(self, frame):
        self.ifShow_LaneGuides = True
        self.laneGuides = {'x':[], '-x':[], 'y':[], '-y':[], 'visual':[]}        # used to guide the vehicles
        self.start      = {'x':[], '-x':[], 'y':[], '-y':[], 'visual':[]}        # where the vehicles start
        self.LightState = None  # define later
        self.f = frame          # the frame is visual.frame object

        # the bricks used to border the road, "fl" means front left, and "fm" means front middle, etc.
        self.bricks = \
            {"fl": None, "fm": None, "fr": None,
             "bl": None, "bm": None, "br": None,
             "lf": None, "lm": None, "lb": None,
             "rf": None, "rm": None, "rb": None
             }
        self.color = (0,1,0)
        self.width = 3.5  # width of the road of one direction, Chinese Standard is 3.75m, 4 lanes
        self.length = 20
        self.brickLength    = self.length    # length of the road we show
        self.brickThickness = 0.5            # thickness of bricks
        self.brickHeight    = 1

        # self.construct_shape()
        self.init_LaneGuides()

    def init_LaneGuides(self):
        def r(v): return -v[1], v[0], v[2]                          # rotate 90 degree
        num_of_lanes = 4
        gap = self.width / num_of_lanes
        direction = vector(1,0,0)
        # not clever enough, so manually specify some values intelligently
        k=2
        for i in range(1, num_of_lanes + 1):
            self.start['x'].append(( -k * self.length,  i * self.width / (num_of_lanes + 1), 0))
            self.start['-x'].append(( k * self.length, -i * self.width / (num_of_lanes + 1), 0))
            self.start['y'].append((  i * self.width / (num_of_lanes + 1), -k * self.length, 0))
            self.start['-y'].append((-i * self.width / (num_of_lanes + 1),  k * self.length, 0))

            #the lanes that go straight
            self.laneGuides[ 'x'].append([( k*self.length, i*self.width/ (num_of_lanes+1), 0)])  # the guide point
            self.laneGuides['-x'].append([(-k*self.length,-i*self.width/ (num_of_lanes+1), 0)])  # -x direction
            self.laneGuides[ 'y'].append([(i*self.width/ (num_of_lanes+1),  k*self.length, 0)])  # y direction, too
            self.laneGuides['-y'].append([(-i*self.width/(num_of_lanes+1), -k*self.length, 0)])

        # the fifth lanes that turn right
        print i # debug
        # the middle guide points
        self.laneGuides['x'].append([(self.laneGuides['-y'][num_of_lanes - 1][0][0], i * self.width / (num_of_lanes + 1), 0)])
        self.laneGuides['-x'].append([(self.laneGuides[ 'y'][num_of_lanes - 1][0][0], -i * self.width / (num_of_lanes + 1), 0)])
        self.laneGuides['y'].append([(i * self.width / (num_of_lanes + 1), self.laneGuides[ 'x'][num_of_lanes - 1][0][1], 0)])
        self.laneGuides['-y'].append([(-i * self.width / (num_of_lanes + 1), self.laneGuides['-x'][num_of_lanes - 1][0][1], 0)])
        k = 2
        self.start['x'].append((-k * self.length, i * self.width / (num_of_lanes + 1), 0))
        self.start['-x'].append((k * self.length, -i * self.width / (num_of_lanes + 1), 0))
        self.start['y'].append((i * self.width / (num_of_lanes + 1), -k * self.length, 0))
        self.start['-y'].append((-i * self.width / (num_of_lanes + 1), k * self.length, 0))
        # the end guide points
        self.laneGuides[ 'x'][-1].append((self.laneGuides['-y'][num_of_lanes - 1][0][0], -k*self.length, 0))
        self.laneGuides['-y'][-1].append((-k*self.length, self.laneGuides['-x'][num_of_lanes - 1][0][1], 0))
        self.laneGuides['-x'][-1].append((self.laneGuides[ 'y'][num_of_lanes - 1][0][0],  k*self.length, 0))
        self.laneGuides[ 'y'][-1].append(( k*self.length, self.laneGuides[ 'x'][num_of_lanes - 1][0][1], 0))
        print self.laneGuides


    def construct_shape(self):
        for idx in self.bricks:
            # create bricks
            self.bricks[idx] = box(color=self.color, frame=self.f,
                                    length=self.brickLength, width=self.brickThickness, height=self.brickHeight,
                                   )
            # place bricks
            if idx[0] == 'f' or idx[0] == 'b':
                self.bricks[idx].rotate(angle=pi/2, axis=self.f.up)
                if idx[1] == 'l':
                    delta = vector(-self.width - self.brickThickness, 0, 0)
                    move(self.bricks[idx], delta)
                elif idx[1] == 'r':
                    delta = vector( self.width + self.brickThickness, 0, 0)
                    move(self.bricks[idx], delta)
            else:
                if idx[1] == 'f':
                    delta = vector(0, self.width + self.brickThickness, 0)
                    move(self.bricks[idx], delta)
                elif idx[1] == 'b':
                    delta = vector(0,-self.width - self.brickThickness, 0)
                    move(self.bricks[idx], delta)

    def invisual_laneGuides(self):
        for obj in self.laneGuides['visual']:
            obj.visible = False

    def visual_laneGuides(self):
        if self.laneGuides['visual'] == []:  # time to initialize
            for i in self.laneGuides:
                print i, self.laneGuides[i]  # debug
                if i == 'visual': continue
                for j in range(len(self.laneGuides[i])):
                    self.laneGuides['visual'].append(curve(pos=[self.start[i][j]] + self.laneGuides[i][j]))
        for obj in self.laneGuides['visual']:
            obj.visible = True



if __name__ == '__main__':
    scene.lights=[]
    scene.ambient = color.gray(0.8)
    scene.forward = (0,1,0)
    scene.up = (0,0,1)
    arrow(axis=vector(1,0,0)*10, color=(1,0,0))
    arrow(axis=vector(0,1,0)*10, color=(0,1,0))
    arrow(axis=vector(0,0,1)*10, color=(0,0,1))
    s = sphere()  # center flag
    crd = CrossRoadShape(frame())

    if crd.ifShow_LaneGuides:
        crd.visual_laneGuides()

    while True:
        rate(1)
        #print "up, forward: ",scene.up, scene.forward